﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace IMMeDotNet {

	/// <summary>
	/// Provides access to various Win32 routines, constants and types for USB HID access.
	/// </summary>
	class Win32 {

		#region Constants

		/// <summary>
		/// Control options that filter the device information elements that are added to the device information set
		/// </summary>
		[Flags]
		public enum DIGCF {
			/// <summary>
			/// Return a list of installed devices for all device setup classes or all device interface classes.
			/// </summary>
			DIGCF_ALLCLASSES = 0x4,
			/// <summary>
			/// Return devices that support device interfaces for the specified device interface classes. This flag must be set in the Flags parameter if the Enumerator parameter specifies a device instance ID.
			/// </summary>
			DIGCF_DEVICEINTERFACE = 0x10,
			/// <summary>
			/// Return only the device that is associated with the system default device interface, if one is set, for the specified device interface classes.
			/// </summary>
			DIGCF_DEFAULT = 0x1,
			/// <summary>
			/// Return only devices that are currently present in a system.
			/// </summary>
			DIGCF_PRESENT = 0x2,
			/// <summary>
			/// Return only devices that are a part of the current hardware profile.
			/// </summary>
			DIGCF_PROFILE = 0x8,
		}

		/// <summary>
		/// Specifies the type of access you need when you are opening a handle to an object.
		/// </summary>
		[Flags]
		public enum GENERIC_ACCESS {
			/// <summary>
			/// Read, write, and execute access.
			/// </summary>
			GENERIC_ALL = 0x10000000,
			/// <summary>
			/// Read access.
			/// </summary>
			GENERIC_READ = unchecked((int)0x80000000),
			/// <summary>
			/// Write access.
			/// </summary>
			GENERIC_WRITE = 0x40000000,
			/// <summary>
			///  Execute access.
			/// </summary>
			GENERIC_EXECUTE = 0x20000000,
		}

		#endregion

		#region Types

		/// <summary>
		/// Defines a device instance that is a member of a device information set.
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct SP_DEVINFO_DATA {
			/// <summary>
			/// The size, in bytes, of the SP_DEVINFO_DATA structure.
			/// </summary>
			public int cbSize;
			/// <summary>
			/// The GUID of the device's setup class.
			/// </summary>
			public Guid ClassGuid;
			/// <summary>
			/// An opaque handle to the device instance (also known as a handle to the devnode).
			/// </summary>
			public uint DevInst;
			public IntPtr Reserved;
		}

		/// <summary>
		/// defines a device interface in a device information set.
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct SP_DEVICE_INTERFACE_DATA {
			/// <summary>
			/// The size, in bytes, of the SP_DEVICE_INTERFACE_DATA structure.
			/// </summary>
			public int cbSize;
			/// <summary>
			/// The GUID for the class to which the device interface belongs.
			/// </summary>
			public Guid InterfaceClassGuid;
			public uint Flags;
			public IntPtr Reserved;
		}

		/// <summary>
		/// Contains the path for a device interface.
		/// </summary>
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		public struct SP_DEVICE_INTERFACE_DETAIL_DATA {
			/// <summary>
			/// The size, in bytes, of the SP_DEVICE_INTERFACE_DETAIL_DATA structure.
			/// </summary>
			public int cbSize;
			/// <summary>
			/// A NULL-terminated string that contains the device interface path. This path can be passed to Win32 functions such as CreateFile.
			/// </summary>
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
			public string DevicePath;
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		public struct HIDP_CAPS {
			public ushort Usage;
			public ushort UsagePage;
			public ushort InputReportByteLength;
			public ushort OutputReportByteLength;
			public ushort FeatureReportByteLength;
			public ushort Reserved1;
			public ushort Reserved2;
			public ushort Reserved3;
			public ushort Reserved4;
			public ushort Reserved5;
			public ushort Reserved6;
			public ushort Reserved7;
			public ushort Reserved8;
			public ushort Reserved9;
			public ushort Reserved10;
			public ushort Reserved11;
			public ushort Reserved12;
			public ushort Reserved13;
			public ushort Reserved14;
			public ushort Reserved15;
			public ushort Reserved16;
			public ushort Reserved17;
			public ushort NumberLinkCollectionNodes;
			public ushort NumberInputButtonCaps;
			public ushort NumberInputValueCaps;
			public ushort NumberInputDataIndices;
			public ushort NumberOutputButtonCaps;
			public ushort NumberOutputValueCaps;
			public ushort NumberOutputDataIndices;
			public ushort NumberFeatureButtonCaps;
			public ushort NumberFeatureValueCaps;
			public ushort NumberFeatureDataIndices;
		}

		#endregion

		#region Routines

		/// <summary>
		/// returns a handle to a device information set that contains requested device information elements for a local computer.
		/// </summary>
		/// <param name="ClassGuid">A pointer to the GUID for a device setup class or a device interface class. This pointer is optional and can be NULL.</param>
		/// <param name="Enumerator">A pointer to a NULL-terminated string that specifies an identifier (ID) of a Plug and Play (PnP) enumerator or a PnP device instance ID.</param>
		/// <param name="hwndParent">A handle to the top-level window to be used for a user interface that is associated with installing a device instance in the device information set. This handle is optional and can be NULL.</param>
		/// <param name="Flags">A variable that specifies control options that filter the device information elements that are added to the device information set.</param>
		/// <returns>If the operation succeeds, SetupDiGetClassDevs returns a handle to a device information set that contains all installed devices that matched the supplied parameters.</returns>
		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, DIGCF Flags);

		/// <summary>
		/// Deletes a device information set and frees all associated memory.
		/// </summary>
		/// <param name="hDevInfo">A handle to the device information set to delete.</param>
		/// <returns>The function returns TRUE if it is successful. Otherwise, it returns FALSE and the logged error can be retrieved with a call to GetLastError.</returns>
		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern bool SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);

		/// <summary>
		/// Enumerates the device interfaces that are contained in a device information set.
		/// </summary>
		/// <param name="hDevInfo">A pointer to a device information set that contains the device interfaces for which to return information. This handle is typically returned by SetupDiGetClassDevs.</param>
		/// <param name="devInfo">A pointer to an SP_DEVINFO_DATA structure that specifies a device information element in DeviceInfoSet.</param>
		/// <param name="interfaceClassGuid">A pointer to a GUID that specifies the device interface class for the requested interface.</param>
		/// <param name="memberIndex">A zero-based index into the list of interfaces in the device information set. The caller should call this function first with MemberIndex set to zero to obtain the first interface. Then, repeatedly increment MemberIndex and retrieve an interface until this function fails and GetLastError returns ERROR_NO_MORE_ITEMS.</param>
		/// <param name="deviceInterfaceData">A pointer to a caller-allocated buffer that contains, on successful return, a completed SP_DEVICE_INTERFACE_DATA structure that identifies an interface that meets the search parameters.</param>
		/// <returns>Returns TRUE if the function completed without error. If the function completed with an error, FALSE is returned and the error code for the failure can be retrieved by calling GetLastError.</returns>
		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

		/// <summary>
		/// Returns details about a device interface.
		/// </summary>
		/// <param name="hDevInfo">A pointer to the device information set that contains the interface for which to retrieve details. This handle is typically returned by SetupDiGetClassDevs.</param>
		/// <param name="deviceInterfaceData">A pointer to an SP_DEVICE_INTERFACE_DATA structure that specifies the interface in DeviceInfoSet for which to retrieve details. A pointer of this type is typically returned by SetupDiEnumDeviceInterfaces.</param>
		/// <param name="deviceInterfaceDetailData">A pointer to an SP_DEVICE_INTERFACE_DETAIL_DATA structure to receive information about the specified interface.</param>
		/// <param name="deviceInterfaceDetailDataSize">The size of the DeviceInterfaceDetailData buffer. The buffer must be at least (offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR)) bytes, to contain the fixed part of the structure and a single NULL to terminate an empty MULTI_SZ string.</param>
		/// <param name="requiredSize">A pointer to a variable of type DWORD that receives the required size of the DeviceInterfaceDetailData buffer. This size includes the size of the fixed part of the structure plus the number of bytes required for the variable-length device path string. This parameter is optional and can be NULL.</param>
		/// <param name="deviceInfoData">A pointer to a buffer that receives information about the device that supports the requested interface. The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA). This parameter is optional and can be NULL.</param>
		/// <returns>Returns TRUE if the function completed without error. If the function completed with an error, FALSE is returned and the error code for the failure can be retrieved by calling GetLastError.</returns>
		[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
		public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, ref SP_DEVINFO_DATA deviceInfoData);

		/// <summary>
		/// Returns the device interface GUID for HIDClass devices.
		/// </summary>
		/// <param name="HidGuid">Pointer to a caller-allocated GUID buffer that the routine uses to return the device interface GUID for HIDClass devices.</param>
		[DllImport("hid.dll")]
		public static extern void HidD_GetHidGuid(out Guid HidGuid);

		/// <summary>
		/// Returns a top-level collection's preparsed data.
		/// </summary>
		/// <param name="hidHandle">Specifies an open handle to a top-level collection.</param>
		/// <param name="preparsedDataPointer">Pointer to the address of a routine-allocated buffer that contains a collection's preparsed data in a _HIDP_PREPARSED_DATA structure.</param>
		/// <returns>TRUE if it succeeds; otherwise, it returns FALSE.</returns>
		[DllImport("hid.dll")]
		public static extern bool HidD_GetPreparsedData(SafeHandle hidHandle, ref IntPtr preparsedDataPointer);

		/// <summary>
		/// Releases the resources that the HID class driver allocated to hold a top-level collection's preparsed data.
		/// </summary>
		/// <param name="PreparsedData">Pointer to the buffer, returned by HidD_GetPreparsedData, that is freed.</param>
		/// <returns>TRUE if it succeeds. Otherwise, it returns FALSE if the buffer was not a preparsed data buffer.</returns>
		[DllImport("hid.dll", SetLastError = true)]
		public static extern bool HidD_FreePreparsedData(ref IntPtr PreparsedData);

		/// <summary>
		/// Returns a top-level collection's HIDP_CAPS structure.
		/// </summary>
		/// <param name="PreparsedData">Pointer to a top-level collection's preparsed data.</param>
		/// <param name="Capabilities">Pointer to a caller-allocated buffer that the routine uses to return a collection's HIDP_CAPS structure.</param>
		/// <returns></returns>
		[DllImport("hid.dll", SetLastError = true)]
		public static extern int HidP_GetCaps(IntPtr PreparsedData, ref HIDP_CAPS Capabilities);

		/// <summary>
		/// Creates or opens a file or I/O device. 
		/// </summary>
		/// <param name="lpFileName">The name of the file or device to be created or opened.</param>
		/// <param name="dwDesiredAccess">The requested access to the file or device, which can be summarized as read, write, both or neither (zero).</param>
		/// <param name="dwShareMode">The requested sharing mode of the file or device, which can be read, write, both, delete, all of these, or none.</param>
		/// <param name="lpSecurityAttributes">A pointer to a SECURITY_ATTRIBUTES structure that contains two separate but related data members: an optional security descriptor, and a Boolean value that determines whether the returned handle can be inherited by child processes.</param>
		/// <param name="dwCreationDisposition">An action to take on a file or device that exists or does not exist.</param>
		/// <param name="dwFlagsAndAttributes">The file or device attributes and flags, FILE_ATTRIBUTE_NORMAL being the most common default value for files.</param>
		/// <param name="hTemplateFile">A valid handle to a template file with the GENERIC_READ access right. The template file supplies file attributes and extended attributes for the file that is being created.</param>
		/// <returns>If the function succeeds, the return value is an open handle to the specified file, device, named pipe, or mail slot.</returns>
		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern SafeFileHandle CreateFile(string lpFileName, GENERIC_ACCESS dwDesiredAccess, FileShare dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition, FileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);

		/// <summary>
		/// Closes an open object handle.
		/// </summary>
		/// <param name="hObject">A valid handle to an open object.</param>
		/// <returns>If the function succeeds, the return value is true. If the function fails, the return value is false.</returns>
		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool CloseHandle(SafeFileHandle hObject);

		#endregion
	}
}
